package com.ctshk.rpc.train.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ctshk.common.enums.IsDeletedCode;
import com.ctshk.rpc.system.req.MainDataStationReq;
import com.ctshk.rpc.system.service.IMainDataStationService;
import com.ctshk.rpc.system.service.ISysAgentService;
import com.ctshk.rpc.system.service.ISysDepartmentService;
import com.ctshk.rpc.system.service.ISysUserService;
import com.ctshk.rpc.train.dto.ReservationTrainPageDTO;
import com.ctshk.rpc.train.dto.ReservationTrainSeatInfoDTO;
import com.ctshk.rpc.train.entity.TrainProduct;
import com.ctshk.rpc.train.entity.TrainStationCode;
import com.ctshk.rpc.train.enums.SeatClsEnum;
import com.ctshk.rpc.train.mapper.*;
import com.ctshk.rpc.train.service.CJTrainProductService;
import com.ctshk.rpc.train.service.ITrainApiService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 城际直通车 火车产品  服务实现类
 */
@Slf4j
@DubboService
public class CJTrainProductServiceImpl extends ServiceImpl<TrainProductMapper, TrainProduct> implements CJTrainProductService {

//    @Value("${CJUrl}")
//    private String urlConstant;
//
//    @Value("${ip}")
//    private String ip;

    private static final String IP = "http://203.90.6.56/cts";

    @Autowired
    private TrainProductMapper trainProductMapper;
    @Autowired
    private TrainProductCostMapper trainProductCostMapper;
    @Autowired
    private TrainProductCostDateMapper trainProductCostDateMapper;
    @Autowired
    private TrainProducCostPriceMapper trainProducCostPriceMapper;
    @Autowired
    private TrainProductReservationSettingsMapper trainProductReservationSettingsMapper;
    @Autowired
    private TrainProductStrategyMapper trainProductStrategyMapper;
    @Autowired
    private TrainProductIncreaseStrategyRuleMapper ruleMapper;
    @Autowired
    private TrainNumberMapper trainNumberMapper;
    @Autowired
    private TrainProductChannelOfflineSelfCustomMapper trainProductChannelOfflineSelfCustomMapper;
    @Autowired
    private TrainProductChannelOnlineSelfPlatformMapper trainProductChannelOnlineSelfPlatformMapper;
    @Autowired
    private TrainProductChannelOnlineThirdPlatformMapper trainProductChannelOnlineThirdPlatformMapper;
    @Autowired
    private TrainProductChannelOfflineAgentCustomMapper trainProductChannelOfflineAgentCustomMapper;
    @Autowired
    private TrainTicketsInitializationSettingsMapper trainTicketsInitializationSettingsMapper;
    @DubboReference
    private ISysDepartmentService sysDepartmentService;
    @DubboReference
    private ISysAgentService sysAgentService;
    @DubboReference
    ISysUserService sysUserService;
    @DubboReference
    private ITrainApiService iTrainApiService;
    @Autowired
    private TrainStationCodeMapper stationCodeMapper;
    @DubboReference
    private IMainDataStationService iMainDataStationService;

    @Override
    public List<Map<String, Object>> getTrainLineList() {
        // 方法名
        String methodName = "mtdListEffStation";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("列出有效車站, 发起请求 url:{}, xml:{}", IP + "/Enquiry", xml);
            res = HttpUtil.post(IP + "/Enquiry", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("列出有效車站 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            if (CollectionUtil.isEmpty(maps)) {
                log.info("列出有效車站接口解析为空 :{}", res);
                return null;
            }

            // 1. 根据以車站列出車次
            List<String> strStatCdeList = new ArrayList<>();
            maps.forEach(map -> {
                if (null != map.get("strStatCde")) {
                    strStatCdeList.add(map.get("strStatCde").toString());
                }
            });

            log.info("strStatCdeList : {}", JSON.toJSONString(strStatCdeList));
            List<Map<String, String>> strStatCdeMapList = getStrCodeList(strStatCdeList);
            log.info("------> {} " + JSON.toJSONString(strStatCdeMapList));

            List<Map<String, Object>> countList = new ArrayList<>();
            strStatCdeMapList.forEach(map -> {
                for (String strCode : map.keySet()) {
                    // todo 时间先写死一天的
                    log.info(strCode + "--------->" + map.get(strCode) + "--------->" + "2021-05-21");
//                    List<Map<String, Object>> trainList = this.getTrainNumberList(strCode, map.get(strCode), "2021-05-21");
//                    countList.addAll(trainList);

//                    if (CollectionUtil.isNotEmpty(trainList)) {
//                        trainList.forEach(train -> {
//                            String trnNum = train.get("strTrnNum").toString();
//                            String strDeptTime = train.get("strDeptTime").toString();
//                            String strRunID = train.get("strRunID").toString();
//                            String strTrnTypeCde = train.get("strTrnTypeCde").toString();
//
//                            // 获取座位号信息
//                            List<Map<String, Object>> trainSeatList =  this.getTrainNumberSeatList(trnNum, strCode, map.get(strCode), "2021-05-21");
//                        });
//                    }
                }
            });
            log.info("---------> :{}", JSON.toJSONString(countList));
            return countList;
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 解析返回文件
        return null;
    }

    @Override
    public void saveTrainStationCode() {
        // 方法名
        String methodName = "mtdListEffStation";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";

        log.info("列出有效車站, 发起请求 url:{}, xml:{}", IP + "/Enquiry", xml);
        res = HttpUtil.post(IP + "/Enquiry", xml, 5000);
        log.info("接口返回xml: {}", res);
        if (StringUtils.isEmpty(res)) {
            log.error("列出有效車站 接口返回值为空");
        }
        // 解析xml
        List<Map<String, Object>> maps = parseXml(res);
        log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
        if (CollectionUtil.isEmpty(maps)) {
            log.info("列出有效車站接口解析为空 :{}", res);
            return;
        }
        List<TrainStationCode> list = new ArrayList<>();
        List<MainDataStationReq> mainDataStationReqs = new ArrayList<>();
        maps.forEach(train ->{
            MainDataStationReq mainDataStationReq = new MainDataStationReq();
            TrainStationCode stationCode = new TrainStationCode();
            stationCode.setStationSimple(train.get("strServCde").toString());
            stationCode.setStationCh(train.get("nstrChiDesc").toString());
            stationCode.setStationEn(train.get("strStatCde").toString());
            stationCode.setIsDeleted(IsDeletedCode.NO.getCode());
            stationCode.setGmtCreate(LocalDateTime.now());
            mainDataStationReq.setStatus(0);
            mainDataStationReq.setStationName(train.get("nstrChiDesc").toString());
            list.add(stationCode);
            mainDataStationReqs.add(mainDataStationReq);
        });
        if (CollectionUtil.isEmpty(list)) {
            return;
        }

        // 删除旧的站点数据
        List<String> stationEnList = list.stream().map(TrainStationCode::getStationEn).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(stationEnList)) {
            stationCodeMapper.delete(new QueryWrapper<TrainStationCode>().lambda().
                    in(TrainStationCode::getStationEn, stationEnList).eq(TrainStationCode::getStationSimple, "GZ"));
        }
        stationCodeMapper.insertBatchSomeColumn(list);
        iMainDataStationService.batchAdd(mainDataStationReqs);
    }

    /**
     * 以車站列出車次
     *
     * @return
     */
    @Override
    public List<ReservationTrainPageDTO> getTrainNumberList(String strFmStatCde, String strToStatCde,
                                                            String boardingStationName, String alightingStationName,String dtmDeptDte) {
        // 方法名
        String methodName = "mtdListTrnByStations";

        // 参数
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("strAgtCde", "CTS");
        paramMap.put("strAgtUnitCde", "CTS");

        // 车站获取 strStatCde , 出发站, 到达站
        paramMap.put("strFmStatCde", strFmStatCde);
        paramMap.put("strToStatCde", strToStatCde);
        paramMap.put("dtmDeptDte", dtmDeptDte);
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("发起请求 url:{}, xml:{}", IP + "/Enquiry", xml);
            res = HttpUtil.post(IP + "/Enquiry", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("以車站列出車次 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            List<ReservationTrainPageDTO> trainDtoList = new ArrayList<>();
            if (CollectionUtil.isEmpty(maps)) {
                return trainDtoList;
            }
            maps.parallelStream().forEach(train -> {
                LocalTime beginTime = LocalTime.parse(train.get("strDeptTime").toString(), DateTimeFormatter.ofPattern("HHmm"));
                LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), beginTime);
                ReservationTrainPageDTO trainPageDTO = new ReservationTrainPageDTO();
                trainPageDTO.setTrainNumberName(train.get("strTrnNum").toString());
                trainPageDTO.setDepartureTime(localDateTime);
                trainPageDTO.setRunningTime("-");
                trainPageDTO.setTerminusTime(null);
                trainPageDTO.setIntendedForName("所有人");
                trainPageDTO.setSupplierName("港铁直通车");
                trainPageDTO.setBoardingStationName(boardingStationName);
                trainPageDTO.setAlightingStationName(alightingStationName);

                List<Map<String, Object>> seatList = this.getTrainNumberSeatList(train.get("strTrnNum").toString(), strFmStatCde, strToStatCde, dtmDeptDte);
                StringBuilder seatLevel = new StringBuilder();
                List<ReservationTrainSeatInfoDTO> seatInfoDTOS = new ArrayList<>();
                AtomicInteger ticketCount = new AtomicInteger();
                seatList.forEach(seat -> {
                    ticketCount.addAndGet(Integer.parseInt(seat.get("intSeatAval").toString()));
                    seatLevel.append(seat.get("nstrChiDesc").toString()).append(",");
                    ReservationTrainSeatInfoDTO infoDTO = new ReservationTrainSeatInfoDTO();
                    infoDTO.setTicketNum(seat.get("intSeatAval").toString());
                    infoDTO.setSeatLevelName(seat.get("nstrChiDesc").toString());
                    infoDTO.setSupplierName("港铁直通车");
                    seatInfoDTOS.add(infoDTO);
                });
                seatLevel.setLength(seatLevel.length() - 1);
                trainPageDTO.setSeatLevelName(seatLevel.toString());
                trainPageDTO.setTicketNum(ticketCount.get());
                trainPageDTO.setReservationTrainSeatInfoDTOList(seatInfoDTOS);
                trainDtoList.add(trainPageDTO);
            });
            return trainDtoList;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 以車次列出座位
     * @return
     */
    @Override
    public List<Map<String, Object>> getTrainNumberSeatList(String strTrnNum, String strFmStatCde, String strToStatCde,
                                                            String dtmDeptDte) {
        // 方法名
        String methodName = "mtdListSeatPerClassByTrnNum";

        // 参数
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");

        // 港铁不会限制传过来的座位池
        paramMap.put("strSeatPoolCde", "MTRC");
        paramMap.put("strTrnNum", strTrnNum);

        // 车站获取 strStatCde , 出发站, 到达站
        paramMap.put("strFmStatCde", strFmStatCde);
        paramMap.put("strToStatCde", strToStatCde);
        paramMap.put("dtmDeptDte", dtmDeptDte);
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("发起请求 url:{}, xml:{}", IP + "/Enquiry", xml);
            res = HttpUtil.post(IP + "/Enquiry", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("以車次列出座位 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            return maps;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 列出车次的车厢及车层
     * @return
     */
    @Override
    public List<Map<String, Object>> listCoachAvalByTrn(String strTrnNum, String strFmStatCde, String strToStatCde,
                                                            String dtmDeptDte, String seatClsCode) {
        // 方法名
        String methodName = "mtdListCoachAvalByTrn";

        // 参数
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("strAgtCde", "CTS");
        paramMap.put("strAgtUnitCde", "CTS");
        paramMap.put("strSeatClsCde", seatClsCode);

        // 港铁不会限制传过来的座位池
        paramMap.put("strSeatPoolCde", "MTRC");
        paramMap.put("strTrnNum", strTrnNum);

        // 车站获取 strStatCde , 出发站, 到达站
        paramMap.put("strFmStatCde", strFmStatCde);
        paramMap.put("strToStatCde", strToStatCde);
        paramMap.put("dtmDeptDte", dtmDeptDte);
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("列出車次的車廂及車層 发起请求 url:{}, xml:{}", IP + "/Enquiry", xml);
            res = HttpUtil.post(IP + "/Enquiry", xml, 5000);
            log.info("列出車次的車廂及車層 接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("列出車次的車廂及車層 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            return maps;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 座位图资料查询
     * @return
     */
    @Override
    public List<Map<String, Object>> listSeatPlanByCoach(String strTrnNum, String strFmStatCde, String strToStatCde,
                                                        String dtmDeptDte, String seatClsCode, String intCoachSeq) {
        // 方法名
        String methodName = "mtdListSeatPlanByCoach";

        // 参数
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("strAgtCde", "CTS");
        paramMap.put("strAgtUnitCde", "CTS");
        paramMap.put("strSeatClsCde", seatClsCode);

        // 港铁不会限制传过来的座位池
        paramMap.put("strSeatPoolCde", "MTRC");
        paramMap.put("strTrnNum", strTrnNum);
        paramMap.put("intCoachSeq", intCoachSeq);

        // 车站获取 strStatCde , 出发站, 到达站
        paramMap.put("strFmStatCde", strFmStatCde);
        paramMap.put("strToStatCde", strToStatCde);
        paramMap.put("dtmDeptDte", dtmDeptDte);
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("座位图资料查询 发起请求 url:{}, xml:{}", IP + "/Enquiry", xml);
            res = HttpUtil.post(IP + "/Enquiry", xml, 5000);
            log.info("座位图资料查询 接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("座位图资料查询 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            return maps;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询车次列表
     */
    public void getListTrnDetailByDateForCTS() {
        // 方法名
        String methodName = "mtdListTrnDetailByDateForCTS";

        // 参数
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("strAgtCde", "CTS");
        paramMap.put("strAgtUnitCde", "CTS");

        // 时间
        paramMap.put("dtmFmDate", "2021-05-20");
        paramMap.put("dtmToDate", "2021-05-21");

        // 车站获取 strStatCde 出发站 - 到达站
        paramMap.put("strFmStatCde", "CP");
        paramMap.put("strToStatCde", "HH");

        // 车次号 todo 车次号先写死
        paramMap.put("strTrnNum", "Z820");
        paramMap.put("strSeatClsCde", SeatClsEnum.S1.getCode());
        paramMap.put("strSeatPoolCde", "MTRC");
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("查询车次列表 发起请求 url:{}, xml:{}", IP + "/Enquiry", xml);
            res = HttpUtil.post(IP + "/Enquiry", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("查询车次列表 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询列出車次路線
     */
    public void getListRouteStationsByTrnNum(String dtmDeptDte) {
        // 方法名
        String methodName = "mtdListRouteStationsByTrnNum";

        // 参数 todo 车次号先写死
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("dtmDeptDte", dtmDeptDte);
        paramMap.put("strTrnNum", "Z820");
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("查询车次列表 发起请求 url:{}, xml:{}", IP + "/Enquiry", xml);
            res = HttpUtil.post(IP + "/Enquiry", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("查询车次列表 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 開始售票
     */
    @Override
    public String begin() {
        // 方法名
        String methodName = "mtdBegin";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("strAgtCde", "CTS");
        paramMap.put("strAgtUnitCde", "CTS");

        // 窗口号 和 售票员的ID 先写死, eg: CounterID= 01 UserID = CTSUSER01
        paramMap.put("strCounterID", "01");
        paramMap.put("strUsrID", "CTSUSER01");

        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("開始售票 发起请求 url:{}, xml:{}", IP + "/Sale", xml);
            res = HttpUtil.post(IP + "/Sale", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("開始售票 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            if (maps.get(0).containsKey("Code")) {
                return maps.get(0).get("Description").toString();
            }
            String strTxnID = maps.get(0).get("strTxnID").toString();
            String dtmProcTime = maps.get(0).get("dtmProcTime").toString();
            String strMsgCde = maps.get(0).get("strMsgCde").toString();
            return strTxnID;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 取消售票或訂票交易
     * @return
     */
    @Override
    public List<Map<String, Object>> rollBack(String strTxnID) {
        // 方法名
        String methodName = "mtdRollBack";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("strTxnID", strTxnID);

        String xml = buildRequestXml(methodName, paramMap);
        String res;
        log.info("取消售票或訂票交易 发起请求 url:{}, xml:{}", IP + "/Sale", xml);
        res = HttpUtil.post(IP + "/Sale", xml, 5000);
        log.info("接口返回xml: {}", res);
        if (StringUtils.isEmpty(res)) {
            log.error("取消售票或訂票交易 接口返回值为空");
        }
        // 解析xml
        List<Map<String, Object>> maps = parseXml(res);
        log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
        return maps;

    }


    /**
     * 自动选座
     *  @param strTxnID    6446781
     * @param fmStatCde   CP
     * @param toStatCde   HH
     * @param dtmDeptDte  时间 yyyymmdd
     * @param trainNum    Z820
     * @param SeatClsCode 座位等级    S1("S1", "一等座"),  SP("SP", "特等座"),;
     * @param tickTypeCde ADULT("NA", "成人票"), CHILD("NC", "小孩票"),;
     * @return
     */
    @Override
    public List<Map<String, Object>> selSeatBySystem(String strTxnID, String fmStatCde, String toStatCde, String dtmDeptDte,
                                                     String trainNum, String SeatClsCode, String tickTypeCde,
                                                     Integer adultNum, Integer childNum) {
        // 方法名
        String methodName = "mtdSelSeatBySystem";

        // 车次号
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strTxnID", strTxnID);
        paramMap.put("dtmDeptDte", dtmDeptDte);
        paramMap.put("strTrnNum", trainNum);
        paramMap.put("strServCde", "GZ");

        // 车站获取 strStatCde 出发站 - 到达站
        paramMap.put("strFmStatCde", fmStatCde);
        paramMap.put("strToStatCde", toStatCde);

        // 座位等级    S1("S1", "一等座"),  SP("SP", "特等座"),;
        paramMap.put("strSeatClsCde", SeatClsCode);
        paramMap.put("strSeatPoolCde", "MTRC");

        // 座位等级  ADULT("NA", "成人票"), CHILD("NC", "小孩票"),;
        StringBuilder ticketTypeParam = new StringBuilder();
        for (int i = 0; i < adultNum; i++) {
            ticketTypeParam.append("\n<strTickTypeCde>NA</strTickTypeCde>\n");
        }
        for (int i = 0; i < childNum; i++) {
            ticketTypeParam.append("\n<strTickTypeCde>NC</strTickTypeCde>\n");
        }
        paramMap.put("astrTickTypeCde", ticketTypeParam.toString());
        paramMap.put("strSeatPropCde", "");

        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("自动选座 发起请求 url:{}, xml:{}", IP + "/Sale", xml);
            res = HttpUtil.post(IP + "/Sale", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("自动选座 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            return maps;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 列出已選座位
     * @return
     */
    @Override
    public List<Map<String, Object>> listReservedSeat(String strTxnID) {
        // 方法名
        String methodName = "mtdListReservedSeat";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("strTxnID", strTxnID);
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("列出已選座位 发起请求 url:{}, xml:{}", IP + "/Sale", xml);
            res = HttpUtil.post(IP + "/Sale", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("列出已選座位 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            return maps;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 更改實名資料 UpdateRealNameInfo
     */
    @Override
    public String updateRealNameInfo(String strTxnID, String strTrnNum,
                                     String intCoachSeq, String intSeatSeq, String dtmTrnDeptDte,
                                     String intDocType, String strDocID, String strDocHolderName,
                                     String intDeckSeq) {
        // 方法名
        String methodName = "mtdUpdateRealNameInfo";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");

        // 出发日期 格式 yyyymmddhhmm
        paramMap.put("dtmDeptDte", dtmTrnDeptDte);
        paramMap.put("strTrnNum", strTrnNum);
        paramMap.put("strTxnID", strTxnID);

        // CoachSeq和SeatSeq是车厢和座位的序号
        paramMap.put("intCoachSeq", intCoachSeq);
        paramMap.put("intSeatSeq", intSeatSeq);

        // 列车的层，1代表Bottom，2 代表Top
        paramMap.put("intDeckSeq", intDeckSeq);

        paramMap.put("intDocType", intDocType); // 证件的类型
        paramMap.put("strDocID", strDocID); // 证件号
        paramMap.put("strDocHolderName", strDocHolderName); // 持有人的名字
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("更改實名資料 发起请求 url:{}, xml:{}", IP + "/Sale", xml);
            res = HttpUtil.post(IP + "/Sale", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("更改實名資料 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 確認付款 mtdPayment
     */
    @Override
    public String payment(String strTxnID,
                          String amount, String strCustName, String strCustTel) {
        // 方法名
        String methodName = "mtdPayment";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");

        // 付款方式字段
        paramMap.put("strPayMethCde", "CRD");
        paramMap.put("strTxnID", strTxnID);

        // 系统加辐过后的金额
        paramMap.put("sintAmount", amount);
        paramMap.put("strCustName", strCustName);
        paramMap.put("strCustTel", strCustTel);
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("確認付款 发起请求 url:{}, xml:{}", IP + "/Sale", xml);
            res = HttpUtil.post(IP + "/Sale", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("確認付款 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 完成售票或訂票交易並確認座位及
     * 關連車票票號 mtdCommit
     */
    @Override
    public String mtdCommit(String strTxnID) {
        // 方法名
        String methodName = "mtdCommit";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");

        // 付款方式字段
        paramMap.put("strNewTickSerNum", "");
        paramMap.put("strTxnID", strTxnID);
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("查询车次列表 发起请求 url:{}, xml:{}", IP + "/Sale", xml);
            res = HttpUtil.post(IP + "/Sale", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("確認付款 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //-----------------退票操作--------------------

    /**
     * 開始退票交易
     * @return
     */
//    @Override
    public String refundBegin() {
        // 方法名
        String methodName = "mtdBegin";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("strAgtCde", "CTS");
        paramMap.put("strAgtUnitCde", "CTS");

        // 窗口号 和 售票员的ID 先写死, eg: CounterID= 01 UserID = CTSUSER01
        paramMap.put("strCounterID", "01");
        paramMap.put("strUsrID", "CTSUSER01");

        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("開始退票交易 发起请求 url:{}, xml:{}", IP + "/Refund", xml);
            res = HttpUtil.post(IP + "/Refund", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("開始退票交易 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            if (maps.get(0).containsKey("Code")) {
                return maps.get(0).get("Description").toString();
            }
            String strTxnID = maps.get(0).get("strTxnID").toString();
            String dtmProcTime = maps.get(0).get("dtmProcTime").toString();
            String strMsgCde = maps.get(0).get("strMsgCde").toString();
            return strTxnID;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String refundAddTick(String strTxnID) {
        // 方法名
        String methodName = "mtdAddTick";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("strTxnID", strTxnID);
        paramMap.put("strTickRefNum", strTxnID);
        paramMap.put("strTickSerNum", strTxnID);
        paramMap.put("strRefdType", strTxnID);
        paramMap.put("strRefdInvNum", strTxnID);

        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("開始退票交易 发起请求 url:{}, xml:{}", IP + "/Refund", xml);
            res = HttpUtil.post(IP + "/Refund", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("開始退票交易 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            if (maps.get(0).containsKey("Code")) {
                return maps.get(0).get("Description").toString();
            }
//            String strTxnID = maps.get(0).get("strTxnID").toString();
//            String dtmProcTime = maps.get(0).get("dtmProcTime").toString();
//            String strMsgCde = maps.get(0).get("strMsgCde").toString();
            return strTxnID;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 完成退款交易
     */
//    @Override
    public String refundCommit(String strTxnID) {
        // 方法名
        String methodName = "mtdCommit";

        // strServCde参数 ""    "BJ"    "GZ"
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("strServCde", "GZ");
        paramMap.put("strTxnID", strTxnID);
        String xml = buildRequestXml(methodName, paramMap);
        String res = "";
        try {
            log.info("完成退款交易 发起请求 url:{}, xml:{}", IP + "/Refund", xml);
            res = HttpUtil.post(IP + "/Refund", xml, 5000);
            log.info("接口返回xml: {}", res);
            if (StringUtils.isEmpty(res)) {
                log.error("完成退款交易 接口返回值为空");
            }
            // 解析xml
            List<Map<String, Object>> maps = parseXml(res);
            log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public static void main(String[] args) {
        String time = "0903";
        LocalTime beginDate = LocalTime.parse(time, DateTimeFormatter.ofPattern("HHmm"));
        LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), beginDate);
        System.out.println(localDateTime); ;
//        String methodName = "mtdListEffStation";
//        Map<String, String> paramMap = new HashMap<>();
//        paramMap.put("strServCde","GZ");
//        paramMap.put("strServCde1", "GZ");
//        System.out.println(buildRequestXml(methodName, paramMap)); ;
//        String xml = "<Result xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" strMsgCde=\"\"><Data strMsgCde=\"\" nstrChiDesc=\"常平\" strEngDesc=\"Changping\" strServCde=\"GZ\" strStatCde=\"CP\" /><Data strMsgCde=\"\" nstrChiDesc=\"佛山\" strEngDesc=\"Foshan\" strServCde=\"GZ\" strStatCde=\"FS\" /><Data strMsgCde=\"\" nstrChiDesc=\"廣州東\" strEngDesc=\"Guangzhoudong\" strServCde=\"GZ\" strStatCde=\"GZE\" /><Data strMsgCde=\"\" nstrChiDesc=\"香港紅磡\" strEngDesc=\"Hong Kong Hung Hom\" strServCde=\"GZ\" strStatCde=\"HH\" /></Result>";
//        String xml = "<Result xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" strMsgCde=\"\"><Data strMsgCde=\"\" nstrChiDesc=\"北京西\" strEngDesc=\"Beijingxi\" strServCde=\"BJ\" strStatCde=\"BJW\" /><Data strMsgCde=\"\" nstrChiDesc=\"香港紅磡\" strEngDesc=\"Hong Kong Hung Hom\" strServCde=\"BJ\" strStatCde=\"HH\" /><Data strMsgCde=\"\" nstrChiDesc=\"上海\" strEngDesc=\"Shanghai\" strServCde=\"BJ\" strStatCde=\"SH\" /><Data strMsgCde=\"\" nstrChiDesc=\"常平\" strEngDesc=\"Changping\" strServCde=\"GZ\" strStatCde=\"CP\" /><Data strMsgCde=\"\" nstrChiDesc=\"佛山\" strEngDesc=\"Foshan\" strServCde=\"GZ\" strStatCde=\"FS\" /><Data strMsgCde=\"\" nstrChiDesc=\"廣州東\" strEngDesc=\"Guangzhoudong\" strServCde=\"GZ\" strStatCde=\"GZE\" /><Data strMsgCde=\"\" nstrChiDesc=\"香港紅磡\" strEngDesc=\"Hong Kong Hung Hom\" strServCde=\"GZ\" strStatCde=\"HH\" /></Result>";
//        String xml = "<Result xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" strMsgCde=\"\"><Data strMsgCde=\"\" nstrChiDesc=\"常平\" strEngDesc=\"Changping\" strServCde=\"GZ\" strStatCde=\"CP\" /><Data strMsgCde=\"\" nstrChiDesc=\"佛山\" strEngDesc=\"Foshan\" strServCde=\"GZ\" strStatCde=\"FS\" /><Data strMsgCde=\"\" nstrChiDesc=\"廣州東\" strEngDesc=\"Guangzhoudong\" strServCde=\"GZ\" strStatCde=\"GZE\" /><Data strMsgCde=\"\" nstrChiDesc=\"香港紅磡\" strEngDesc=\"Hong Kong Hung Hom\" strServCde=\"GZ\" strStatCde=\"HH\" /></Result>";
//        List<Map<String, Object>> maps = parseXml(toUTF8(xml));
//        System.out.println(maps);
//        List<String> strStatCdeList = new ArrayList<>();
//        assert maps != null;
//        maps.forEach(map -> {
//            if (null != map.get("strStatCde")) {
//                strStatCdeList.add(map.get("strStatCde").toString());
//            }
//        });
//        List<Map<String, String>> strStatCdeMapList = getStrCodeList(strStatCdeList);
//        strStatCdeMapList.parallelStream().forEach(map -> {
//            for (String strCode : map.keySet()) {
//                System.out.println(strCode +"--------->"+ map.get(strCode)+"--------->"+"2021-05-20");
//            }
//        });
//        System.out.println(parseXml(res));
//        log.info("解析xml 结果: {}", maps != null ? JSON.toJSONString(maps) : "null");
    }

    /**
     * 获取车站组合
     *
     * @param myList
     * @return
     */
    private static List<Map<String, String>> getStrCodeList(List<String> myList) {
        // 有重复车站, 先去重
        myList = myList.stream().distinct().collect(Collectors.toList());
        List<Map<String, String>> strCodeMapList = new ArrayList<>();
        for (int i = 0; i < myList.size() - 1; i++) {
            for (String s : myList) {
                // 过滤出发站和到达站一致的数据
                if (myList.get(i).equals(s)) {
                    continue;
                }
                Map<String, String> map = new HashMap<>();
                map.put(myList.get(i), s);
                strCodeMapList.add(map);
            }
        }
        return strCodeMapList;
    }


    /**
     * 构建请求参数
     */
    private static String buildRequestXml(String methodName, Map<String, String> paramMap) {
        StringBuilder stringBuilder = new StringBuilder();
        for (String key : paramMap.keySet()) {
            stringBuilder.append("        <").append(key).append(">").append(paramMap.get(key)).append("</").append(key).append(">\n");
        }
        return "<?xml version=\"1.0\" encoding=\"BIG5\"?>\n" +
                "<Action strMethName=\"" + methodName + "\">\n" +
                "    <Data>\n" +
                stringBuilder.toString() +
                "    </Data>\n" +
                "</Action>";
    }


    /**
     * 返回xml转为解析为map
     */
    public static List<Map<String, Object>> parseXml(String xml) {
        try {
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
            Element root = document.getRootElement();
            List<Map<String, Object>> resMap = new ArrayList<>();

            List<Element> list = root.elements();
            log.info("-------------> list.size: {}", list.size());
            for (Element ele : list) {
                // 获取所有子元素
                Map<String, Object> map = new HashMap<>();
                for (Iterator i = ele.attributeIterator(); i.hasNext(); ) {
                    Attribute attribute = (Attribute) i.next();
                    map.put(attribute.getName(), attribute.getText());
                }
                resMap.add(map);
            }
            return resMap;
        } catch (DocumentException e) {
            log.error(e.getMessage());
        }
        return null;
    }


    private static String mapToXml(Map<String, String> data) {
        StringWriter writer = new StringWriter();
        try {
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
            documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
            documentBuilderFactory.setXIncludeAware(false);
            documentBuilderFactory.setExpandEntityReferences(false);
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            org.w3c.dom.Document document = documentBuilder.newDocument();
            org.w3c.dom.Element root = document.createElement("xml");
            document.appendChild(root);
            for (String key : data.keySet()) {
                String value = data.get(key);
                if (value == null) {
                    value = "";
                }
                value = value.trim();
                org.w3c.dom.Element filed = document.createElement(key);
                filed.appendChild(document.createTextNode(value));
                root.appendChild(filed);
            }
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            DOMSource source = new DOMSource(document);
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");

            StreamResult result = new StreamResult(writer);
            transformer.transform(source, result);
            String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return output;
        } catch (TransformerException | ParserConfigurationException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将字符串的编码格式转换为utf-8
     *
     * @param str
     * @return Name = new
     * String(Name.getBytes("ISO-8859-1"), "utf-8");
     */
    public static String toUTF8(String str) {
        if (StringUtils.isEmpty(str)) {
            return "";
        }
        try {
            if (str.equals(new String(str.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8))) {
                return str;
            }
            if (str.equals(new String(str.getBytes("GB2312"), "GB2312"))) {
                str = new String(str.getBytes("GB2312"), StandardCharsets.UTF_8);
                return str;
            }
            if (str.equals(new String(str.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.ISO_8859_1))) {
                str = new String(str.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
                return str;
            }
            if (str.equals(new String(str.getBytes("GBK"), "GBK"))) {
                str = new String(str.getBytes("GBK"), StandardCharsets.UTF_8);
                return str;
            }
        } catch (Exception ignored) {

        }
        return str;
    }
}
